/*=============================================================================
    Spirit v1.6.0
    Copyright (c) 2002-2003 Hartmut Kaiser
    http://spirit.sourceforge.net/

    Permission to copy, use, modify, sell and distribute this software is
    granted provided this copyright notice appears in all copies. This
    software is provided "as is" without express or implied warranty, and
    with no claim as to its suitability for any purpose.
=============================================================================*/
#ifndef BOOST_SPIRIT_LISTS_HPP
#define BOOST_SPIRIT_LISTS_HPP

///////////////////////////////////////////////////////////////////////////////
#include "boost/spirit/core/meta/impl/parser_type.hpp"
#include "boost/spirit/core/parser.hpp"
#include "boost/spirit/core/composite/composite.hpp"
#include "boost/spirit/utility/impl/lists.ipp"

///////////////////////////////////////////////////////////////////////////////
namespace boost { namespace spirit {

///////////////////////////////////////////////////////////////////////////////
//
//  list_parser class
//
//      List parsers allow to parse constructs like
//
//          item >> *(delim >> item)
//
//      where 'item' is an auxiliary expression to parse and 'delim' is an
//      auxiliary delimiter to parse.
//
//      The list_parser class also can match an optional closing delimiter
//      represented by the 'end' parser at the end of the list:
//
//          item >> *(delim >> item) >> !end.
//
//      If ItemT is an action_parser_category type (parser with an attached
//      semantic action) we have to do something special. This happens, if the
//      user wrote something like:
//
//          list_p(item[f], delim)
//
//      where 'item' is the parser matching one item of the list sequence and
//      'f' is a functor to be called after matching one item. If we would do
//      nothing, the resulting code would parse the sequence as follows:
//
//          (item[f] - delim) >> *(delim >> (item[f] - delim))
//
//      what in most cases is not what the user expects.
//      (If this _is_ what you've expected, then please use one of the list_p
//      generator functions 'direct()', which will inhibit re-attaching
//      the actor to the item parser).
//
//      To make the list parser behave as expected:
//
//          (item - delim)[f] >> *(delim >> (item - delim)[f])
//
//      the actor attached to the 'item' parser has to be re-attached to the
//      *(item - delim) parser construct, which will make the resulting list
//      parser 'do the right thing'.
//
//      Additionally special care must be taken, if the item parser is a
//      unary_parser_category type parser as
//
//          list_p(*anychar_p, ',')
//
//      which without any refactoring would result in
//
//          (*anychar_p - ch_p(','))
//              >> *( ch_p(',') >> (*anychar_p - ch_p(',')) )
//
//      and will not give the expected result (the first *anychar_p will eat up
//      all the input up to the end of the input stream). So we have to
//      refactor this into:
//
//          *(anychar_p - ch_p(','))
//              >> *( ch_p(',') >> *(anychar_p - ch_p(',')) )
//
//      what will give the correct result.
//
//      The case, where the item parser is a combination of the two mentioned
//      problems (i.e. the item parser is a unary parser  with an attached
//      action), is handled accordingly too:
//
//          list_p((*anychar_p)[f], ',')
//
//      will be parsed as expected:
//
//          (*(anychar_p - ch_p(',')))[f]
//              >> *( ch_p(',') >> (*(anychar_p - ch_p(',')))[f] ).
//
///////////////////////////////////////////////////////////////////////////////
template <
    typename ItemT, typename DelimT, typename EndT = no_list_endtoken,
    typename CategoryT = plain_parser_category
>
struct list_parser :
    public parser<list_parser<ItemT, DelimT, EndT, CategoryT> > {

    typedef list_parser<ItemT, DelimT, EndT, CategoryT> self_t;
    typedef CategoryT parser_category_t;

    list_parser(ItemT const &item_, DelimT const &delim_,
        EndT const& end_ = no_list_endtoken())
    : item(item_), delim(delim_), end(end_)
    {}

    template <typename ScannerT>
    typename parser_result<self_t, ScannerT>::type
    parse(ScannerT const& scan) const
    {
        return impl::list_parser_type<CategoryT>
            ::parse(scan, *this, item, delim, end);
    }

private:
    typename as_parser<ItemT>::type::embed_t item;
    typename as_parser<DelimT>::type::embed_t delim;
    typename as_parser<EndT>::type::embed_t end;
};

///////////////////////////////////////////////////////////////////////////////
//
//  List parser generator template
//
//      This is a helper for generating a correct list_parser<> from
//      auxiliary parameters. There are the following types supported as
//      parameters yet: parsers, single characters and strings (see
//      as_parser<> in parser_type.hpp).
//
//      The list_parser_gen by itself can be used for parsing comma separated
//      lists without item formatting:
//
//          list_p.parse(...)
//              matches any comma separated list.
//
//      If list_p is used with one parameter, this parameter is used to match
//      the delimiter:
//
//          list_p(';').parse(...)
//              matches any semicolon separated list.
//
//      If list_p is used with two parameters, the first parameter is used to
//      match the items and the second parameter matches the delimiters:
//
//          list_p(uint_p, ',').parse(...)
//              matches comma separated unsigned integers.
//
//      If list_p is used with three parameters, the first parameter is used
//      to match the items, the second one is used to match the delimiters and
//      the third one is used to match an optional ending token sequence:
//
//          list_p(real_p, ';', eol_p).parse(...)
//              matches a semicolon separated list of real numbers optionally
//              followed by an end of line.
//
//      The list_p in the previous examples denotes the predefined parser
//      generator, which should be used to define list parsers (see below).
//
///////////////////////////////////////////////////////////////////////////////

template <typename CharT = char>
struct list_parser_gen :
    public list_parser<kleene_star<anychar_parser>, chlit<CharT> >
{
    typedef list_parser_gen<CharT> self_t;

// construct the list_parser_gen object as an list parser for comma separated
// lists without item formatting.
    list_parser_gen()
    : list_parser<kleene_star<anychar_parser>, chlit<CharT> >
        (*anychar_p, chlit<CharT>(','))
    {}

// The following generator functions should be used under normal circumstances.
// (the operator()(...) functions)

    // Generic generator functions for creation of concrete list parsers, which
    // support 'normal' syntax:
    //
    //      item >> *(delim >> item)
    //
    // If item isn't given, everything between two delimiters is matched.

    template<typename DelimT>
    list_parser<
        kleene_star<anychar_parser>,
        typename as_parser<DelimT>::type,
        no_list_endtoken,
        unary_parser_category      // there is no action to re-attach
    >
    operator()(DelimT const &delim_) const
    {
        typedef kleene_star<anychar_parser> item_t;
        typedef typename as_parser<DelimT>::type delim_t;

        typedef
            list_parser<item_t, delim_t, no_list_endtoken, unary_parser_category>
            return_t;

        return return_t(*anychar_p, as_parser<DelimT>::convert(delim_));
    }

    template<typename ItemT, typename DelimT>
    list_parser<
        typename as_parser<ItemT>::type,
        typename as_parser<DelimT>::type,
        no_list_endtoken,
        typename as_parser<ItemT>::type::parser_category_t
    >
    operator()(ItemT const &item_, DelimT const &delim_) const
    {
        typedef typename as_parser<ItemT>::type item_t;
        typedef typename as_parser<DelimT>::type delim_t;
        typedef list_parser<item_t, delim_t, no_list_endtoken,
                BOOST_SPIRIT_TYPENAME item_t::parser_category_t>
            return_t;

        return return_t(
            as_parser<ItemT>::convert(item_),
            as_parser<DelimT>::convert(delim_)
        );
    }

    // Generic generator function for creation of concrete list parsers, which
    // support 'extended' syntax:
    //
    //      item >> *(delim >> item) >> !end

    template<typename ItemT, typename DelimT, typename EndT>
    list_parser<
        typename as_parser<ItemT>::type,
        typename as_parser<DelimT>::type,
        typename as_parser<EndT>::type
    >
    operator()(
        ItemT const &item_, DelimT const &delim_, EndT const &end_) const
    {
        typedef typename as_parser<ItemT>::type item_t;
        typedef typename as_parser<DelimT>::type delim_t;
        typedef typename as_parser<EndT>::type end_t;

        typedef list_parser<item_t, delim_t, end_t,
                BOOST_SPIRIT_TYPENAME item_t::parser_category_t>
            return_t;

        return return_t(
            as_parser<ItemT>::convert(item_),
            as_parser<DelimT>::convert(delim_),
            as_parser<EndT>::convert(end_)
        );
    }

// The following functions should be used, if the 'item' parser has an attached
// semantic action or is a unary_parser_category type parser and the structure
// of the resulting list parser should _not_ be refactored during parser
// construction (see comment above).

    // Generic generator function for creation of concrete list parsers, which
    // support 'normal' syntax:
    //
    //      item >> *(delim >> item)

    template<typename ItemT, typename DelimT>
    list_parser<
        typename as_parser<ItemT>::type,
        typename as_parser<DelimT>::type,
        no_list_endtoken,
        plain_parser_category        // inhibit action re-attachment
    >
    direct(ItemT const &item_, DelimT const &delim_) const
    {
        typedef typename as_parser<ItemT>::type item_t;
        typedef typename as_parser<DelimT>::type delim_t;
        typedef list_parser<item_t, delim_t, no_list_endtoken,
                plain_parser_category>
            return_t;

        return return_t(
            as_parser<ItemT>::convert(item_),
            as_parser<DelimT>::convert(delim_)
        );
    }

    // Generic generator function for creation of concrete list parsers, which
    // support 'extended' syntax:
    //
    //      item >> *(delim >> item) >> !end

    template<typename ItemT, typename DelimT, typename EndT>
    list_parser<
        typename as_parser<ItemT>::type,
        typename as_parser<DelimT>::type,
        typename as_parser<EndT>::type,
        plain_parser_category        // inhibit action re-attachment
    >
    direct(
        ItemT const &item_, DelimT const &delim_, EndT const &end_) const
    {
        typedef typename as_parser<ItemT>::type item_t;
        typedef typename as_parser<DelimT>::type delim_t;
        typedef typename as_parser<EndT>::type end_t;

        typedef
            list_parser<item_t, delim_t, end_t, plain_parser_category>
            return_t;

        return return_t(
            as_parser<ItemT>::convert(item_),
            as_parser<DelimT>::convert(delim_),
            as_parser<EndT>::convert(end_)
        );
    }
};

///////////////////////////////////////////////////////////////////////////////
//
//  Predefined list parser generator
//
//      The list_p parser generator can be used
//        - by itself for parsing comma separated lists without item formatting
//      or
//        - for generating list parsers with auxiliary parser parameters
//          for the 'item', 'delim' and 'end' subsequences.
//      (see comment above)
//
///////////////////////////////////////////////////////////////////////////////
const list_parser_gen<> list_p = list_parser_gen<>();

///////////////////////////////////////////////////////////////////////////////
}} // namespace boost::spirit

#endif
